﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Security;

using BOSS.Dao.Security;
using BOSS.Domain.Security;
using BOSS.Service.Caching;
using BOSS.Service.Implements.Caching;
using BOSS.Service.Implements.Security;
using BOSS.Service.Security;

using Spring.Context.Support;
using Spring.Transaction.Interceptor;

namespace BOSS.Service.Security
{
    /// <summary>
    /// 登录信息注册表
    /// </summary>
    public class LoginInfoRegistry
    {
        /// <summary>
        /// 唯一实例
        /// </summary>
        private static readonly LoginInfoRegistry instance = new LoginInfoRegistry();

        static LoginInfoRegistry()
        {
            LoginInfoDao = (ILoginInfoDao)ContextRegistry.GetContext().GetObject("LoginInfoDao");
        }

        internal static ILoginInfoDao LoginInfoDao { get; set; }

        /// <summary>
        /// 获取唯一实例
        /// </summary>
        public static LoginInfoRegistry Instance
        {
            get
            {
                return instance;
            }
        }

        /// <summary>
        /// 登录信息集合
        /// </summary>
        private ICategoryCache<string, LoginInfo> m_loginInfos;

        /// <summary>
        /// 自动重新登录
        /// </summary>
        private bool m_autoRelogin = false;

        /// <summary>
        /// 构造函数
        /// </summary>
        private LoginInfoRegistry()
        {
            ICacheService cacheService = new AspNetCacheService();
            this.m_loginInfos = cacheService.GetCategoryCache<string, LoginInfo>("loginInfos");
            this.AutoRelogin = true;
        }

        /// <summary>
        /// 丢失登录信息事件
        /// </summary>
        private event EventHandler MissLoginInfo;

        /// <summary>
        /// 注册登录信息
        /// </summary>
        /// <param name="loginInfo"></param>
        /// <returns></returns>
        internal string Registry(LoginInfo loginInfo)
        {
            string key = loginInfo.Guid.ToString();
            LoginInfoRegistry.LoginInfoDao.Save(loginInfo);
            this.m_loginInfos.Add(key, loginInfo);
            return key;
        }

        /// <summary>
        /// 取消登录信息的注册
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        internal LoginInfo UnRegistry(string key)
        {
            LoginInfo loginInfo = this.m_loginInfos.Get(key);
            if (loginInfo != null)
            {
                LoginInfoRegistry.LoginInfoDao.Delete(loginInfo);
                this.m_loginInfos.Remove(key);
                return loginInfo;
            }
            return null;
        }

        /// <summary>
        /// 获取登录信息
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        internal LoginInfo Retrieve(string key, string ip)
        {
            LoginInfo loginInfo = this.m_loginInfos.Get(key);
            if (loginInfo == null)
            {
                loginInfo = LoginInfoRegistry.LoginInfoDao.FindByGuid(new Guid(key), ip);
                LoginInfoRegistry.Instance.Registry(loginInfo);
            }
            if (loginInfo != null)
            {
                loginInfo.RefreshTime = DateTime.Now;
                LoginInfoRegistry.LoginInfoDao.SaveOrUpdate(loginInfo);
                return loginInfo;
            }
            return null;
        }

        /// <summary>
        /// 获取当前的登录信息（不会为 null）
        /// </summary>
        public LoginInfo Current
        {
            get
            {
                if (HttpContext.Current == null)
                {
                    throw new NotSupportedException("请在 Web 程序中使用。");
                }

                LoginInfo loginInfo = this.GetLoginInfo(true);
                if (loginInfo != null)
                {
                    return loginInfo;
                }

                if (this.MissLoginInfo != null)
                {
                    this.MissLoginInfo(this, EventArgs.Empty);
                }

                HttpContext.Current.Response.End();
                return null;
            }
        }

        /// <summary>
        /// 获取登录信息（可能为 null）
        /// </summary>
        /// <param name="clearInvalidTicket">true 表示在找不到登录信息的情况下，清除登录 Cookie</param>
        /// <returns>null 表示尚未登录</returns>
        [Transaction(ReadOnly = false)]
        public LoginInfo GetLoginInfo(bool clearInvalidTicket)
        {
            HttpContext context = HttpContext.Current;
            if (context == null)
            {
                return null;
            }

            HttpCookie cookie = context.Request.Cookies[FormsAuthentication.FormsCookieName];
            if (cookie != null)
            {
                if (!string.IsNullOrEmpty(cookie.Value))
                {
                    FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookie.Value);
                    if (ticket != null)
                    {
                        LoginInfo loginInfo = Retrieve(ticket.UserData, context.Request.UserHostAddress);
                        if (loginInfo != null)
                        {
                            return loginInfo;
                        }
                    }
                }

                if (clearInvalidTicket)
                {
                    FormsAuthenticationHelper.SignOut();
                }
            }

            return null;
        }

        /// <summary>
        /// 获取 / 设置是否自动重新登录。默认为 true，即自动重新登录
        /// </summary>
        internal bool AutoRelogin
        {
            get
            {
                return this.m_autoRelogin;
            }
            set
            {
                if (this.m_autoRelogin != value)
                {
                    this.m_autoRelogin = value;
                    if (value)
                    {
                        this.MissLoginInfo += new EventHandler(AutoReloginHandler);
                    }
                    else
                    {
                        this.MissLoginInfo -= new EventHandler(AutoReloginHandler);
                    }
                }
            }
        }

        private void AutoReloginHandler(object sender, EventArgs e)
        {
            if (HttpContext.Current != null)
            {
                FormsAuthenticationHelper.SignOut();
                FormsAuthenticationHelper.RedirectToLoginPage();
                HttpContext.Current.Response.End();
            }
        }
    }
}
